package com.bzframework.pro.api.core.manager;

import cn.hutool.core.util.IdUtil;
import com.bzframework.pro.api.core.provider.ApiSupport;
import com.bzframework.pro.api.core.provider.ApiSupportProvider;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author bingzhong
 * @Date 2025/2/28 11:31
 * @Description api支持管理
 */
public class ApiSupportManager {

    private static final Map<String, List<ApiSupport>> apiSupportsMap = new ConcurrentHashMap<>();

    public void register(String id, List<ApiSupport> apiSupports) {
        apiSupportsMap.put(id, apiSupports);
    }

    public Map<String, List<ApiSupport>> getApiSupportsMap() {
        return apiSupportsMap;
    }

    public void remove(String id) {
        apiSupportsMap.remove(id);
    }

    @SneakyThrows
    public Flux<ApiSupport> loadJar(String id, String location) {
        location = String.format("jar:file:%s!/**/*.class", location);

        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver() {
            @Override
            protected boolean isJarResource(@NonNull Resource resource) {
                return true;
            }
        };
        CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
        Resource[] classes = resourcePatternResolver.getResources(location);
        List<MetadataReader> classResources = new ArrayList<>(classes.length);
        for (Resource aClass : classes) {
            classResources.add(metadataReaderFactory.getMetadataReader(aClass));
        }
        URL url = new File(location).toURI().toURL();
        URLClassLoader classLoader = new URLClassLoader(
                new URL[]{url},
                // 使用当前线程的类加载器作为父类加载器
                Thread.currentThread().getContextClassLoader()
        );

        return Flux.fromIterable(classResources)
                   .flatMap(metadataReader -> registerApi(id, classLoader, metadataReader))
                   .doFinally(signalType -> {
                       metadataReaderFactory.clearCache();
                       try {
                           classLoader.close();
                       } catch (IOException e) {
                           throw new RuntimeException(e);
                       }
                   });
    }

    public Flux<ApiSupport> registerApi(String id, URLClassLoader classLoader, MetadataReader metadataReader) {
        String className = metadataReader.getClassMetadata().getClassName();
        try {
            Class<?> aClass = classLoader.loadClass(className);
            if (!ApiSupportProvider.class.isAssignableFrom(aClass) || aClass.isInterface()) {
                return Flux.empty();
            }
            ApiSupportProvider apiSupportProvider = (ApiSupportProvider) aClass
                    .getDeclaredConstructor()
                    .newInstance();
            return apiSupportProvider
                    .create()
                    .collectList()
                    .flatMapMany(supports -> {
                        // 修改api
                        if (id != null) {
                            register(id, supports);
                            return Flux.fromIterable(supports);
                        }
                        register(IdUtil.fastSimpleUUID(), supports);
                        return Flux.fromIterable(supports);
                    });
        } catch (Exception e) {
            return Flux.empty();
        }
    }

}
